import { NodePath } from '@babel/traverse'

/**
 * 转换require为 import 写法
 */

import generate from '@babel/generator'
import { parse } from '@babel/parser'
import traverse from '@babel/traverse'
import * as types from '@babel/types'

import fs from 'node:fs'
import path from 'node:path'

const target = path.resolve(__dirname, '../demo/08.js')

const content = fs.readFileSync(target, {
	encoding: 'utf-8',
})

let ast = parse(content, {
	sourceType: 'module',
})

/**
 * 监控属性节点，进行操作
 * @param path
 */
const handleNode = (path) => {
	// 一定要断言这里的node类型，node是一个泛型
	const node = path.node as types.ObjectProperty

	// 需要给key 做一个类型限定
	if (types.isIdentifier(node.key) && node.key.name === 'component') {
		// 找到对象key为 component才处理

		// 找到为箭头函数的 节点
		if (types.isArrowFunctionExpression(node.value)) {
			const arrayExpression = node.value as types.ArrowFunctionExpression

			const body = arrayExpression.body as types.CallExpression

			if (types.isArrayExpression(body.arguments[0])) {
				if (
					body.arguments[0].elements.length &&
					types.isStringLiteral(body.arguments[0].elements[0])
				) {
					// 拿到里面的字符串，也就是组件的路径的定义
					const routePathStr = body.arguments[0].elements[0].value

					// 重新构造一个AST节点。 将这块内容替换为 import写法
					const newNode = genArrowFunction(routePathStr)

					// 替换
					path.node.value = newNode
				}
			}
		}
	}
}

// 第二种操作方式，监控箭头函数 钩子， 需要对 path做一个类型判断，否则，下面的 replaceWith 方法无法出现
const changeByArrowFunctionExpression = (
	path: NodePath<types.ArrowFunctionExpression>
) => {
	// 从函数调体里面拿到 组件的路径

	const node = path.node as types.ArrowFunctionExpression

	const body = node.body as types.CallExpression

	if (!body.arguments) return

	if (types.isArrayExpression(body.arguments[0])) {
		let elements = body.arguments[0].elements as Array<types.StringLiteral>

		// 拿到路径字符串
		let routePathStr = elements[0].value

		if (!routePathStr) return

		// 重新构造一个AST节点。 将这块内容替换为 import写法
		const newNode = genArrowFunction(routePathStr)

		// 节点替换
		path.replaceWith(newNode)
	}
}

/**
 * 重新生成一个箭头函数
 * @param str
 */
function genArrowFunction(str: string): types.ArrowFunctionExpression {
	return types.arrowFunctionExpression(
		[],
		types.importExpression(types.stringLiteral(str))
	)
}

traverse(ast, {
	/**
	 * 监听属性
	 * @param path
	 */
	// Property(path) {
	// 	handleNode(path)
	// },

	/**
	 * 监听箭头函数
	 */
	ArrowFunctionExpression(path) {
		changeByArrowFunctionExpression(path)
	},
})

// 生成代码
let dir = path.resolve(__dirname, '../demo/08-1.js')

fs.writeFileSync(dir, generate(ast).code)
